package file

import (
	"bufio"
	"github.com/dop251/goja"
	"github.com/dop251/goja_nodejs/require"
	"io"
	"io/fs"
	"io/ioutil"
	"os"
	"path/filepath"
	"reflect"
)

type Module struct {
	runtime *goja.Runtime
}

type FileState struct {
	FileName string
	IsDir    bool
}

// 判断所给路径文件/文件夹是否存在
func (h Module) Exists(path string) bool {
	_, err := os.Stat(path) //os.Stat获取文件信息
	if err != nil {
		if os.IsExist(err) {
			return true
		}
		return false
	}
	return true
}

// 判断所给路径是否为文件夹
func (h Module) IsDir(path string) bool {
	s, err := os.Stat(path)
	if err != nil {
		return false
	}
	return s.IsDir()
}

// 判断所给路径是否为文件
func (h Module) IsFile(path string) bool {
	return !h.IsDir(path)
}

//创建文件
func (h Module) CreateFile(path string) bool {
	create, err := os.Create(path)
	if err != nil {
		return false
	}
	create.Close()
	return true
}

//创建文件夹
func (h Module) CreateDir(path string) bool {
	err := os.Mkdir(path, os.ModePerm)
	if err != nil {
		return false
	}
	return true
}

//创建多级文件夹
func (h Module) CreateDirAll(path string) bool {
	err := os.MkdirAll(path, os.ModePerm)
	if err != nil {
		return false
	}
	return true
}

//判断文件夹是否存在不存在创建文件夹在创建文件
func (h Module) CreateFileWithDirs(pathStr string) bool {
	//提取文件夹
	dir, _ := filepath.Split(pathStr)
	if !h.IsDir(dir) {
		//创建文件
		h.CreateDirAll(dir)
	}
	create, err := os.Create(pathStr)
	if err != nil {
		return false
	}
	create.Close()
	return true
}

//文件列表
func (h Module) ListFile(path string) []FileState {
	flist := make([]FileState, 1)
	dir, _ := ioutil.ReadDir(path)
	for _, f := range dir {
		flist = append(flist, FileState{FileName: f.Name(), IsDir: f.IsDir()})
	}
	return flist
}

//删除文件
func (h Module) Remove(path string) bool {
	err := os.RemoveAll(path)
	if err != nil {
		return false
	}
	return true
}

//读取文件为字符串
func (h Module) Read(path string) string {
	b, e := ioutil.ReadFile(path)
	if e != nil {
		return ""
	}
	return string(b)
}

//读取文件为字符串
func (h Module) ReadLine(path string) []string {
	fi, err := os.Open(path)
	if err != nil {
		return nil
	}
	defer fi.Close()

	br := bufio.NewReader(fi)
	st := make([]string, 1)
	for {
		a, _, c := br.ReadLine()
		if c == io.EOF {
			break
		}
		st = append(st, string(a))
	}
	return st
}

//读取字节数组
func (h Module) ReadBytes(path string) []byte {
	b, e := ioutil.ReadFile(path)
	if e != nil {
		return nil
	}
	return b
}

//写文件
func (h Module) Write(path string, content interface{}, op int) bool {
	if op == 0 {
		op = 777
	}
	of := reflect.TypeOf(content)
	var e error
	if of.String() == "string" {
		e = ioutil.WriteFile(path, []byte(content.(string)), fs.FileMode(op))
	} else {
		e = ioutil.WriteFile(path, content.([]byte), fs.FileMode(op))
	}
	if e != nil {
		return false
	}
	return true
}

//追加写
func (h Module) WriteAppend(path string, content interface{}, op int) bool {
	if op == 0 {
		op = 0644
	}
	fl, err := os.OpenFile(path, os.O_APPEND|os.O_CREATE, os.FileMode(op))
	defer fl.Close()
	if err != nil {
		return false
	}
	of := reflect.TypeOf(content)
	var e error
	if of.String() == "string" {
		write := bufio.NewWriter(fl)
		_, e = fl.WriteString(content.(string))
		write.Flush()
	} else {
		write := bufio.NewWriter(fl)
		_, e = fl.Write(content.([]byte))
		write.Flush()
	}
	if e == nil {
		return true
	}
	return false
}

//复制文件
func (h Module) Copy(from, to string) bool {
	f, e := os.Stat(from)
	if e != nil {
		return false
	}
	if f.IsDir() {
		//from是文件夹，那么定义to也是文件夹
		if list, e := ioutil.ReadDir(from); e == nil {
			for _, item := range list {
				if !h.Copy(filepath.Join(from, item.Name()), filepath.Join(to, item.Name())) {
					return false
				}
			}
		}
	} else {
		//from是文件，那么创建to的文件夹
		p := filepath.Dir(to)
		if _, e = os.Stat(p); e != nil {
			if e = os.MkdirAll(p, 0777); e != nil {
				return false
			}
		}
		//读取源文件
		file, e := os.Open(from)
		if e != nil {
			return false
		}
		defer file.Close()
		bufReader := bufio.NewReader(file)
		// 创建一个文件用于保存
		out, e := os.Create(to)
		if e != nil {
			return false
		}
		defer out.Close()
		// 然后将文件流和文件流对接起来
		_, e = io.Copy(out, bufReader)
	}
	return true
}

func Require(runtime *goja.Runtime, module *goja.Object) {
	h := &Module{
		runtime: runtime,
	}
	obj := module.Get("exports").(*goja.Object)
	obj.Set("IsFile", h.IsFile)
	obj.Set("IsDir", h.IsDir)
	obj.Set("Exists", h.Exists)
	obj.Set("CreateFile", h.CreateFile)
	obj.Set("CreateFileWithDirs", h.CreateFileWithDirs)
	obj.Set("CreateDir", h.CreateDir)
	obj.Set("CreateDirAll", h.CreateDirAll)
	obj.Set("ListFile", h.ListFile)
	obj.Set("Remove", h.Remove)
	obj.Set("Copy", h.Copy)
	obj.Set("Read", h.Read)
	obj.Set("ReadLine", h.ReadLine)
	obj.Set("ReadBytes", h.ReadBytes)
	obj.Set("Write", h.Write)
	obj.Set("WriteAppend", h.WriteAppend)
}

func Enable(runtime *goja.Runtime) {
	runtime.Set("file", require.Require(runtime, "file"))
}
